/*
 * Copyright (C) 2010 mAPPn.Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mol.market;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.concurrent.ConcurrentHashMap;

import android.app.Activity;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;

import com.mol.market.common.download.DownloadManager;
import com.mol.market.common.download.DownloadManager.Impl;
import com.mol.market.common.util.CashbackManager;
import com.mol.market.common.util.DBUtils;
import com.mol.market.common.util.MarketProvider;
import com.mol.market.common.util.Pair;
import com.mol.market.common.util.SecurityUtil;
import com.mol.market.common.util.ThemeManager;
import com.mol.market.common.util.Utils;
import com.mol.market.common.vo.DownloadInfo;
import com.mol.market.common.vo.UpgradeInfo;

/**
 * 
 * The Client Seesion Object for GfanMobile, contains some necessary information.
 * 
 * @author andrew
 * @date 2010-12-22
 * @since Version 0.5.1
 * 
 */
public class Session extends Observable {

    /** Log tag */
    private final static String TAG = "Session";

    /** Application Context */
    private Context mContext;

    /** The application debug mode */
    public boolean isDebug;

    /** The application uid */
    private String uid;
    
    private String installedPkg;

    /** The mobile device screen size */
    private String screenSize;

    /** The version of OS */
    private int osVersion;

    /** The Http User-Agent */
    private String userAgent;

    /** The user login status */
    private boolean isLogin;

    /** The channel id */
    private String cid;

    /** The SDK id */
    private String cpid;

    /** The Application Debug flag */
    private String debugType;

    /** The Application Version Code */
    private int versionCode;

    /** The Application package name */
    private String packageName;

    /** The Application version name */
    private String versionName;

    /** The Application version name */
    private String appName;

    /** The mobile IMEI code */
    private String imei;

    /** The mobile sim code */
    private String sim;

    /** The mobile mac address */
    private String macAddress;

    /**
     * The mobile model such as "Nexus One" Attention: some model type may have illegal characters
     */
    private String model;

    /** The user-visible version string. E.g., "1.0" */
    private String buildVersion;

    /** User login name */
    private String userName;

    /** User login type */
    private int loginType;

    /** User login password */
    private String password;

    /** Indicate whether new version is available */
    private boolean isUpdateAvailable;

    /** The new version name */
    private String updateVersionName;

    /** The new version code */
    private int updateVersionCode;

    /** The new version description */
    private String updateVersionDesc;

    /** The new version update uri */
    private String updateUri;

    /** The new version update level(Force Update/Option Update) */
    private int updateLevel;

    /** The new version APK download task id */
    private long updateId;

    /** The cloud service device bind flag */
    private boolean isDeviceBinded;

    /** The mobile device id */
    private String deviceId;

    /** The apps upgrade number */
    private int upgradeNumber;

    /** The apps update check time */
    private long updataCheckTime;

    /** The local card version */
    private int creditCardVersion;

    /** The current version */
    private int lastVersion;

    /** 上次更新splash的时间戳 */
    private long splashTime;

    /** 上次更新splash的id */
    private long splashId;

    /** 标记是否是第一次登录应用 */
    private boolean isFirstLogin;

    /** The application list which user has installed */
    private HashSet<String> mInstalledApps;
    
    /** 已经安装的应用 */
    public HashMap<String, HashMap<String, Object>> mPreloadInstalled = new HashMap<String, HashMap<String,Object>>(0);

    /** 图片格式 */
    private int imageFormat;

    /**
     * 屏幕密度
     */
    public float density = 1.5f;

    /**
     * Tab的左Margin 110px
     */
    public int mTabMargin110 = (int) (74 * density);

    /**
     * Tab的左Margin 72px
     */
    public int mTabMargin72 = (int) (48 * density);

    /**
     * Tab的右Margin 9px
     */
    public int mTabMargin9 = (int) (6 * density);

    /**
     * Gallery的宽度
     */
    public int mGalleryItemWidth = (int) (116 * density);

    /**
     * Gallery的高度
     */
    public int mGalleryItemHeight = (int) (200 * density);

    /** Download Manager */
    private DownloadManager mDownloadManager;

    /** The singleton instance */

    private static Session mInstance;

    /** 默认的支付方式 */
    private String mDefaultChargeType;

    /** 消息处理器 */

    private Handler mHandler;

    /** 标示是否有下载任务 */
    private boolean mHasDownloadingTask;

    /** 标示是否提醒更新 */
    private boolean mIsShowUpdateNotification;

    /** 标示是否提醒推送应用 */
    private boolean mIsShowPushApp;

    /** 标示是否自动删除更新包 */
    private boolean mIsAutoDelete;

    /** 标示上次推送的时间 */
    private String mLastNotifyTime;

    /** 上次推送消息的时间 */
    private String mLastPushMessageTime;

    /** 主题 */
    private int mTheme;

    /** 标示是否无图模式 */
    private boolean mIsShowImage;
    
    /** 标示是否在下载OTA */
    public boolean mIsDownloadOta;
    
    /** 标识是否有硬键盘 */
    public boolean mIsKeyboardAvailable;
    
    /** 返利平台管理器 */
    public CashbackManager cashbackManager;
    
    /** 是否是第二次登录*/
    private boolean isSecondLogin;
    
    /** 用户最后登录时间*/
    private long userLastLoginTime;
    
    /** 是否弹出过老虎机提示的Toast*/
    private boolean mIsShowTipAtSecondLogin;
    
    /** 是否点击过精品 */
    private boolean mIsShowHighQualityTabAnimtion;

    /** 是否已经进入应用，避免检查更新多次 */
    public boolean mIsInApp;

	private String mTopPid;

    /**
     * default constructor
     * 
     * @param context
     */
    private Session(Context context) {

        initMessageHandler();

        mContext = context;
        
        mPreference = PreferenceManager.getDefaultSharedPreferences(mContext);

        mHandler.sendEmptyMessage(CURSOR_CREATED);

        osVersion = Build.VERSION.SDK_INT;
        buildVersion = Build.VERSION.RELEASE;
        try {
            model = URLEncoder.encode(Build.MODEL, "UTF-8");
        } catch (UnsupportedEncodingException e) {
        }
        mDownloadManager = new DownloadManager(context.getContentResolver(), getPackageName());

        readSettings();
    }


    public static synchronized Session get(Context context) {

        if (mInstance == null) {
            mInstance = new Session(context);
        }
        return mInstance;
    }

    /**
     * Init the message queue
     */
    private void initMessageHandler() {

        mHandler = new Handler(Looper.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {

                switch (msg.what) {

                case CURSOR_CREATED:

                    mDownloadingList = new ConcurrentHashMap<String, DownloadInfo>();
                    startQuery();
                    break;

                case CURSOR_CHANGED:

                    if (mDownloadingCursor == null) {
                        return;
                    }

                    refreshDownloadApp(mDownloadingCursor);
                    break;

                case CURSOR_UPDATE:

                    setChanged();
                    notifyObservers(Constants.INFO_UPDATE);
                    break;

                case CURSOR_INSTALL_CHANGED:

                    setChanged();
                    notifyObservers(Constants.INFO_INSTALL_CHANGED);
                    break;

                default:
                    break;
                }
            }
        };
    }

    /*
     * 读取用户所有的设置
     */
    private void readSettings() {

        isLogin = mPreference.getBoolean(P_ISLOGIN, false);

        String uidString = mPreference.getString(P_UID, null);
        uid = TextUtils.isEmpty(uidString) ? "" : SecurityUtil.decrypt(uidString);
        
        String installedPkgString = mPreference.getString(P_INSTALLPKG, null);
        installedPkg = TextUtils.isEmpty(uidString) ? "" : SecurityUtil.decrypt(installedPkgString);

        String username = mPreference.getString(P_MARKET_USERNAME, null);
        userName = TextUtils.isEmpty(username) ? "" : SecurityUtil.decrypt(username);

        String pw = mPreference.getString(P_MARKET_PASSWORD, null);
        password = TextUtils.isEmpty(pw) ? "" : SecurityUtil.decrypt(pw);

        // 云推送设置
        isDeviceBinded = mPreference.getBoolean(P_LPNS_IS_BINDED, false);
        deviceId = mPreference.getString(P_LPNS_BINDED_DEVID, "");

        // 更新检查信息
        upgradeNumber = mPreference.getInt(P_UPGRADE_NUM, 0);
        updataCheckTime = mPreference.getLong(P_PRODUCT_UPDATE_CHECK_TIMESTAMP, -1);
        updateId = mPreference.getLong(P_UPDATE_ID, -1);

        // 启动页信息
        splashId = mPreference.getLong(P_SPLASH_ID, -1L);
        splashTime = mPreference.getLong(P_SPLASH_TIME, 0L);

        // 是否第一次登录
        isFirstLogin = mPreference.getBoolean(P_ISFIRST_LOGIN, true);
        
        // 是否是第二次登录
        isSecondLogin = mPreference.getBoolean(P_ISSECOND_LOGIN, false);
        // 是否弹出过关于老虎机的Toast
        mIsShowTipAtSecondLogin = mPreference.getBoolean(P_ISSHOWTIP_ATSECOND, false);
        // 是否显示精品标签的动画
        mIsShowHighQualityTabAnimtion = mPreference.getBoolean(P_ISSHOW_HIGHQUALITYTAB_ANIMTION, true);

        // 充值方式(电话卡或者支付宝)
        mDefaultChargeType = mPreference.getString(P_DEFAULT_CHARGE_TYPE, null);

        // 当前充值卡版本
        lastVersion = mPreference.getInt(P_CURRENT_VERSION, -1);
        // 充值卡信息
        creditCardVersion = mPreference.getInt(P_CARD_VERSION, -1);

        // 通知栏提醒更新信息
        mIsShowUpdateNotification = mPreference.getBoolean(P_UPDATE_APP_NOTIFICATION, true);

        // 通知栏提醒推送应用
        mIsShowPushApp = mPreference.getBoolean(P_RECOMMEND_APP_NOTIFICATION, true);

        // 无图模式
        mIsShowImage = mPreference.getBoolean(P_NOT_DOWNLOAD_IMAGE, false);

        // 是否自动删除安装包
        mIsAutoDelete = mPreference.getBoolean(P_AUTO_DELETE, true);

        // 上次推送时间
        mLastNotifyTime = mPreference.getString(P_RECOMMEND_APP_NOTIFICATION_CHECK_TIME, "0");

        // 上次推送消息时间
        mLastPushMessageTime = mPreference.getString(P_PUSH_MESSAGE_TIMESTAMP, "0");
        
        // 首页推荐pid
        mTopPid = mPreference.getString(P_TOP_PID, "0");

        // 最后登录的时间
        userLastLoginTime = mPreference.getLong(P_LASTLOGIN, 0);
        // 默认的主题
        mTheme = mPreference.getInt(P_THEME, ThemeManager.THEME_TYPE_DARK);
        getApplicationInfo();
    }

    public String getLastPushMessageTime() {
        return mLastPushMessageTime;
    }

    public void setLastPushTime(String time) {
        mLastPushMessageTime = time;
        writePreference(new Pair<String, Object>(P_PUSH_MESSAGE_TIMESTAMP, time));
    }
    
    public String getTopPid() {
    	return mTopPid;
    }
    
    public void setTopPid(String topPid) {
    	mTopPid = topPid;
        writePreference(new Pair<String, Object>(P_TOP_PID, topPid));
    }
    
    public String getInstallPkg() {
        return installedPkg;
    }

    public void setInstallPkg(String pkg) {
    	if(!TextUtils.isEmpty(installedPkg)) {
    		installedPkg = installedPkg + ";" + pkg;
    	} else {
    		installedPkg = pkg;
    	}
        writePreference(new Pair<String, Object>(P_INSTALLPKG, installedPkg));
    }

    public boolean isFirstLogin() {
        return isFirstLogin;
    }
    
    public boolean isSecondLogin(){
        return isSecondLogin;
    }

    public void setSecondLogin(boolean flag){
        isSecondLogin = flag;
        writePreference(new Pair<String, Object>(P_ISSECOND_LOGIN, flag));
    }

    public void setFirstLogin(boolean flag) {
        isFirstLogin = flag;
        writePreference(new Pair<String, Object>(P_ISFIRST_LOGIN, flag));
    }

    // 自动过滤
    public int isFilterApps() {
        // return isFilterApp ? 0 : 1;
        return 1;
    }

    public boolean hasDownloadTask() {
        if (mIsDownloadOta) {
            return true;
        }
        return mHasDownloadingTask;
    }

    public void setDownloadStatus(boolean flag) {
        mHasDownloadingTask = flag;
    }

    /**
     * 通知提醒应用更新
     */
    public boolean isNotificationUpdateApps() {
        return mIsShowUpdateNotification;
    }

    public void setNotificationUpdateApps(boolean flag) {
        mIsShowUpdateNotification = flag;
        writePreference(new Pair<String, Object>(P_UPDATE_APP_NOTIFICATION, flag));
    }

    /**
     * 是否无图模式
     */
    public boolean isStopDownloadImage() {
        return mIsShowImage;
    }

    public void setStopDownloadImage(boolean flag) {
        mIsShowImage = flag;
        writePreference(new Pair<String, Object>(P_NOT_DOWNLOAD_IMAGE, flag));
    }

    /**
     * 是否通知推荐应用
     */
    public boolean isNotificationRecommendApps() {
        return mIsShowPushApp;
    }

    public void setNotificationRecommendApps(boolean flag) {
        mIsShowPushApp = flag;
        writePreference(new Pair<String, Object>(P_RECOMMEND_APP_NOTIFICATION, flag));
    }

    /**
     * 设置检查更新时间
     */
    public void setNotificationTime(String time) {
        mLastNotifyTime = time;
        writePreference(new Pair<String, Object>(P_RECOMMEND_APP_NOTIFICATION_CHECK_TIME,
                mLastNotifyTime));
    }

    /**
     * 获取检查更新时间
     */
    public String getLastNotificationTime() {
        return mLastNotifyTime;
    }

    /**
     * 设置主题
     */
    public void setTheme(int theme) {
        mTheme = theme;
        writePreference(new Pair<String, Object>(P_THEME, mTheme));
    }

    public int getTheme() {
        return mTheme;
    }

    /**
     * 设置系统图片格式
     */
    public void setImageFormat(int value) {
        imageFormat = value;
    }

    /**
     * 获取系统图片格式
     */
    public int getImageFormat() {
        return imageFormat;
    }

    /**
     * 是否自动删除APK文件
     */
    public boolean isAutoDelete() {
        return mIsAutoDelete;
    }

    public void setAutoDelete(boolean flag) {
        mIsAutoDelete = flag;
        writePreference(new Pair<String, Object>(P_AUTO_DELETE, mIsAutoDelete));
    }

    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {

        this.uid = uid;
        writePreference(new Pair<String, Object>(P_UID, uid));
    }

    public String getScreenSize() {
        return screenSize;
    }
    
    public int screenWidth;
    public int screenHeight;

    public void setScreenSize(Activity activity) {

        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;
        screenSize = dm.widthPixels < dm.heightPixels ? dm.widthPixels + "#" + dm.heightPixels
                : dm.heightPixels + "#" + dm.widthPixels;
        density = dm.density;
        mTabMargin110 = (int) (74 * density);
        mTabMargin72 = (int) (48 * density);
        mTabMargin9 = (int) (6 * density);
        mGalleryItemHeight = (int) (200 * density);
        mGalleryItemWidth = (int) (116 * density);
    }

    public int getOsVersion() {
        return osVersion;
    }

    public String getJavaApiUserAgent() {
        if (TextUtils.isEmpty(userAgent)) {
            StringBuilder buf = new StringBuilder();
            final String splash = "/";
            buf.append(getModel()).append(splash).append(getBuildVersion()).append(splash)
                    .append(mContext.getString(R.string.app_name_en)).append(splash)
                    .append(getVersionName()).append(splash).append(getCid()).append(splash)
                    // 2011/3/7 add mac address for Analytics
                    .append(getIMEI()).append(splash).append(getSim()).append(splash)
                    .append(getMac());
            return buf.toString();
        }
        return userAgent;
    }


    public String getUCenterApiUserAgent() {
        return "packageName=com.mol.market,appName=GFanMobile,channelID=9";
    }

    /*
     * 读取机锋市场配置信息
     */
    private void getApplicationInfo() {

        final PackageManager pm = mContext.getPackageManager();
        try {
            final PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), 0);
            versionName = pi.versionName;
            versionCode = pi.versionCode;

            final ApplicationInfo ai = pm.getApplicationInfo(mContext.getPackageName(),
                    PackageManager.GET_META_DATA);
            cid = ai.metaData.get("gfan_cid").toString();
            cpid = ai.metaData.get("gfan_cpid").toString();
            debugType = ai.metaData.get("gfan_debug").toString();

            if ("1".equals(debugType)) {
                // developer mode
                isDebug = true;
            } else if ("0".equals(debugType)) {
                // release mode
                isDebug = false;
            }
            Utils.sDebug = isDebug;

            appName = String.valueOf(ai.loadLabel(pm));
            Utils.sLogTag = appName;
            packageName = mContext.getPackageName();

            TelephonyManager telMgr = (TelephonyManager) mContext
                    .getSystemService(Context.TELEPHONY_SERVICE);
            imei = telMgr.getDeviceId();
            sim = telMgr.getSimSerialNumber();
        } catch (NameNotFoundException e) {
            Log.d(TAG, "met some error when get application info");
        }
    }

    public String getCid() {
        if (TextUtils.isEmpty(cid)) {
            getApplicationInfo();
        }
        return cid;
    }

    public String getCpid() {
        if (TextUtils.isEmpty(cpid)) {
            getApplicationInfo();
        }
        return cpid;
    }

    public String getVersionName() {
        if (TextUtils.isEmpty(versionName)) {
            getApplicationInfo();
        }
        return versionName;
    }

    public int getVersionCode() {
        if (versionCode <= 0) {
            getApplicationInfo();
        }
        return versionCode;
    }

    public String getIMEI() {
        if (TextUtils.isEmpty(imei)) {
            getApplicationInfo();
        }
        return imei;
    }

    public String getPackageName() {
        if (TextUtils.isEmpty(packageName)) {
            getApplicationInfo();
        }
        return packageName;
    }

    public String getSim() {
        if (TextUtils.isEmpty(sim)) {
            getApplicationInfo();
        }
        return sim;
    }

    public String getMac() {
        if (TextUtils.isEmpty(macAddress)) {
            WifiManager wifi = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
            WifiInfo info = wifi.getConnectionInfo();
            macAddress = info.getMacAddress();
        }
        return macAddress;
    }

    public boolean isLogin() {
        return isLogin;
    }

    public void setLogin(boolean isLogin) {

        // there is no need to update for [same] value
        if (this.isLogin == isLogin) {
            return;
        }

        this.isLogin = isLogin;
        writePreference(new Pair<String, Object>(P_ISLOGIN, isLogin));
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {

        this.userName = userName;
        writePreference(new Pair<String, Object>(P_MARKET_USERNAME, userName));
    }

    public int getLoginType() {
        return loginType;
    }

    public void setLoginType(int type) {

        this.loginType = type;
        writePreference(new Pair<String, Object>(P_MARKET_LOGIN_TYPE, type));
    }
    
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {

        this.password = password;
        writePreference(new Pair<String, Object>(P_MARKET_PASSWORD, password));
    }

    public String getAppName() {
        return appName;
    }

    public boolean isUpdateAvailable() {
        return isUpdateAvailable;
    }

    public void setUpdateAvailable(boolean flag) {
        this.isUpdateAvailable = flag;
    }

    public String getUpdateVersionName() {
        return updateVersionName;
    }

    public int getUpdateVersionCode() {
        return updateVersionCode;
    }

    public String getUpdateVersionDesc() {
        return updateVersionDesc;
    }

    public String getUpdateUri() {
        return updateUri;
    }

    public int getUpdateLevel() {
        return updateLevel;
    }

    public void setUpdateInfo(String versionName, int versionCode, String description, String url,
            int level) {
        this.isUpdateAvailable = true;
        this.updateVersionName = versionName;
        this.updateVersionCode = versionCode;
        this.updateVersionDesc = description;
        this.updateUri = url;
        this.updateLevel = level;
    }

    public long getUpdateId() {
        return updateId;
    }

    public void setUpdateID(long updateId) {

        if (this.updateId == updateId) {
            return;
        }

        this.updateId = updateId;
        writePreference(new Pair<String, Object>(P_UPDATE_ID, updateId));
    }

    public String getModel() {
        return model;
    }

    public String getBuildVersion() {
        return buildVersion;
    }

    public DownloadManager getDownloadManager() {
        if (mDownloadManager == null) {
            mDownloadManager = new DownloadManager(mContext.getContentResolver(), getPackageName());
        }
        return mDownloadManager;
    }

    public boolean isDeviceBinded() {
        return isDeviceBinded;
    }

    public void setDeviceBinded(boolean isDeviceBinded) {

        // there is no need to update for [same] value
        if (this.isDeviceBinded == isDeviceBinded) {
            return;
        }

        this.isDeviceBinded = isDeviceBinded;
        writePreference(new Pair<String, Object>(P_LPNS_IS_BINDED, isDeviceBinded));
    }

    public String getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(String deviceId) {

        this.deviceId = deviceId;
        writePreference(new Pair<String, Object>(P_LPNS_BINDED_DEVID, deviceId));
    }

    public int getUpgradeNumber() {
        return upgradeNumber;
    }

    public void setUpgradeNumber(int upgradeNumber) {

        // there is no need to update for [same] value
        if (this.upgradeNumber == upgradeNumber) {
            return;
        }
        this.upgradeNumber = upgradeNumber;
        writePreference(new Pair<String, Object>(P_UPGRADE_NUM, upgradeNumber));
        mHandler.sendEmptyMessage(CURSOR_UPDATE);
    }

    public void minusUpgradeNumber() {
        this.upgradeNumber--;
        writePreference(new Pair<String, Object>(P_UPGRADE_NUM, upgradeNumber));
    }

    public long getUpdataCheckTime() {
        return updataCheckTime;
    }

    public void setUpdataCheckTime(long updataCheckTime) {

        // there is no need to update for [same] value
        if (this.updataCheckTime == updataCheckTime) {
            return;
        }
        this.updataCheckTime = updataCheckTime;
        writePreference(new Pair<String, Object>(P_PRODUCT_UPDATE_CHECK_TIMESTAMP, updataCheckTime));
    }

    public int getCreditCardVersion() {
        return creditCardVersion;
    }

    public void setCreditCardVersion(int creditCardVersion) {

        // there is no need to update for [same] value
        if (this.creditCardVersion == creditCardVersion) {
            return;
        }

        this.creditCardVersion = creditCardVersion;
        writePreference(new Pair<String, Object>(P_CARD_VERSION, creditCardVersion));
    }

    public String getDebugType() {
        return debugType;
    }

    public void close() {
        synchronized (this) {
            if (mDownloadingCursor != null) {
                mDownloadingCursor.unregisterContentObserver(mCursorObserver);
                mDownloadingCursor.close();
            }
            mPreloadInstalled = null;
            mInstance = null;
        }
    }

    private List<PackageInfo> mInstalledAppList;

    /**
     * 已经安装的应用列表信息
     */
    public List<PackageInfo> getInstalledAppsInfo() {

        if (mInstalledAppList == null) {
            mInstalledAppList = Utils.getInstalledApps(mContext);
        }
        return mInstalledAppList;
    }

    public HashSet<String> getInstalledApps() {

        if (mInstalledApps == null) {
            mInstalledAppList = Utils.getInstalledApps(mContext);
        }
        
        return mInstalledApps;
    }

    public void addInstalledApp(String packageName) {

        if (mInstalledApps == null) {
            mInstalledAppList = Utils.getInstalledApps(mContext);
        }
        // 2011/2/21 fix bug
        mInstalledApps.add(packageName);
        HashMap<String, Object> item = new HashMap<String, Object>();
        item.put(Constants.KEY_PRODUCT_PACKAGE_NAME, packageName);
        mPreloadInstalled.put(packageName, Utils.getAppInfo(mContext, packageName));
        mHandler.sendEmptyMessage(CURSOR_INSTALL_CHANGED);
    }

    public void removeInstalledApp(String packageName) {

        if (mInstalledApps == null) {
            mInstalledAppList = Utils.getInstalledApps(mContext);
        }
        // 2011/2/21 fix bug
        mInstalledApps.remove(packageName);
        mPreloadInstalled.remove(packageName);
        mHandler.sendEmptyMessage(CURSOR_INSTALL_CHANGED);
    }

    public void setInstalledApps(HashSet<String> installedApps) {
        this.mInstalledApps = installedApps;
    }

    public long getSplashTime() {
        return splashTime;
    }

    public void setSplashTime(long splashTime) {
        this.splashTime = splashTime;
        writePreference(new Pair<String, Object>(P_SPLASH_TIME, splashTime));
    }

    public int getLastVersion() {
        return lastVersion;
    }

    public void setLastVersion(int currentVersion) {

        if (currentVersion == this.lastVersion) {
            return;
        }
        clearData();
        this.lastVersion = currentVersion;
        writePreference(new Pair<String, Object>(P_CURRENT_VERSION, currentVersion));
    }

    /**
     * 清除上一个版本数据
     */
    public void clearData() {
        setDeviceId("");
        setDeviceBinded(false);
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
        sp.edit().clear().commit();
    }

    public long getSplashId() {
        return splashId;
    }

    public void setSplashId(long splashId) {
        this.splashId = splashId;
        writePreference(new Pair<String, Object>(P_SPLASH_ID, splashId));
    }

    public String getDefaultChargeType() {
        return mDefaultChargeType;
    }

    public void setDefaultChargeType(String type) {
        mDefaultChargeType = type;
        writePreference(new Pair<String, Object>(P_DEFAULT_CHARGE_TYPE, type));
    }
    
    /** 创建下载数据结果集 */
    private static final int CURSOR_CREATED = 0;
    /** 更新下载数据结果集 */
    private static final int CURSOR_CHANGED = 1;
    /** 产品更新 */
    private static final int CURSOR_UPDATE = 2;
    /** 已安装产品变更 */
    private static final int CURSOR_INSTALL_CHANGED = 3;

    private ConcurrentHashMap<String, DownloadInfo> mDownloadingList;
    /** The application list which user can update */
    private ConcurrentHashMap<String, UpgradeInfo> mUpdateApps = new ConcurrentHashMap<String, UpgradeInfo>();


    private static HashSet<Object> mPendingDownloadList = new HashSet<Object>();


    private Cursor mDownloadingCursor;
    /** 签名不一致的应用管理列表 */

    public HashMap<String, String> mNotSameApps = new HashMap<String, String>();

    public ConcurrentHashMap<String, DownloadInfo> getDownloadingList() {
        return mDownloadingList;
    }

    public ConcurrentHashMap<String, UpgradeInfo> getUpdateList() {
        if (mUpdateApps == null || mUpdateApps.size() == 0) {
            mUpdateApps = DBUtils.queryUpdateProduct(mContext);
        }
        return mUpdateApps;
    }

    public void setUpdateList() {
        mUpdateApps = DBUtils.queryUpdateProduct(mContext);
        mHandler.sendEmptyMessage(CURSOR_UPDATE);
        mHandler.sendEmptyMessage(CURSOR_CHANGED);
    }

    public void setUpdateList(ConcurrentHashMap<String, UpgradeInfo> list) {
        mUpdateApps = list;
        mHandler.sendEmptyMessage(CURSOR_UPDATE);
    }

    public void removeUpdateItem(String packageNmae) {
        mUpdateApps.remove(packageNmae);
        minusUpgradeNumber();
        mHandler.sendEmptyMessage(CURSOR_UPDATE);
    }


    private ContentObserver mCursorObserver = new ContentObserver(mHandler) {
        @Override
        public void onChange(boolean selfChange) {
            mHandler.sendEmptyMessage(CURSOR_CHANGED);
        }
    };

    private void startQuery() {
        DbStatusRefreshTask refreshTask = new DbStatusRefreshTask(mContext.getContentResolver());

        String selection = MarketProvider.COLUMN_P_IGNORE + "=?";
        String[] selectionArgs = new String[] { "0" };
        refreshTask.startQuery(DbStatusRefreshTask.UPDATE, null, MarketProvider.UPDATE_CONTENT_URI,
                null, selection, selectionArgs, null);

        refreshTask.startQuery(DbStatusRefreshTask.DOWNLOAD, null,
                DownloadManager.Impl.CONTENT_URI, null, "((" + DownloadManager.Impl.COLUMN_STATUS
                        + " >= '190' AND " + DownloadManager.Impl.COLUMN_STATUS + " < '600') AND "
                        + DownloadManager.Impl.COLUMN_DESTINATION + " = '"
                        + DownloadManager.Impl.DESTINATION_EXTERNAL + "' AND "
                        + Impl.COLUMN_MIME_TYPE + " = '"
                        + com.mol.market.common.download.Constants.MIMETYPE_APK + "' AND "
                        + Impl.COLUMN_SOURCE + " = '"
                        + com.mol.market.common.download.Constants.DOWNLOAD_FROM_MARKET + "')",
                null, DownloadManager.Impl.COLUMN_ID + " ASC");
    }

    /**
     * 主动刷新数据
     */
    public void notifyDataChanged() {
        mHandler.sendEmptyMessage(CURSOR_CHANGED);
    }

    /*
     * 创建一个新的下载项目
     */

    private DownloadInfo newDownloadInfo(long id, String packageName, String appName, int source,
            String extras) {

        DownloadInfo item = new DownloadInfo();
        item.id = id;
        item.mPackageName = packageName;
        item.mAppName = appName;
        // 下载来源,用于修复无法清除推送的或者社区的下载项目
        item.mSource = source;
        if (source == com.mol.market.common.download.Constants.DOWNLOAD_FROM_BBS) {
            item.mIconUrl = mContext.getResources().getDrawable(
                    R.drawable.manager_installed_bbs_icon);
        } else if (source == com.mol.market.common.download.Constants.DOWNLOAD_FROM_CLOUD) {
            item.mIconUrl = mContext.getResources().getDrawable(
                    R.drawable.manager_installed_soft_icon);
        } else {
            item.mIconUrl = extras;
        }
        if (TextUtils.isEmpty(packageName)) {
            // 无法获取包名的下载项目
            item.mKey = String.valueOf(item.id);
        } else {
            // 可以获得包名的下载项目
            item.mKey = packageName;
        }
        synchronized (mDownloadingList) {
            mDownloadingList.put(item.mKey, item);
        }
        return item;
    }

    /*
     * 1 拦截用户已经取消的项目，避免无效的刷新 2 拦截用户 检查下载项目的状态, 如果非正常则跳过此项目的刷新
     * 
     * @return False 继续刷新 True 跳过此项目
     */
    private boolean checkInvalidStatus(DownloadInfo info, int newStatus) {

        if (info == null) {
            // 正常情况下这个条件应该不会满足
            return true;
        }

        if (newStatus == DownloadManager.Impl.STATUS_CANCELED) {
            mDownloadManager.remove(info.id);
            return true;
        }
        return false;
    }

    /*
     * 刷新下载中的项目状态
     */
    private static void getRunningStatus(DownloadInfo info, Cursor cursor) {
        long currentBytes = cursor.getInt(cursor
                .getColumnIndex(DownloadManager.Impl.COLUMN_CURRENT_BYTES));
        long totalBytes = cursor.getInt(cursor
                .getColumnIndex(DownloadManager.Impl.COLUMN_TOTAL_BYTES));
        info.mTotalSize = totalBytes;
        info.mCurrentSize = currentBytes;
        int progress = (int) ((float) currentBytes / (float) totalBytes * 100);
        info.mProgress = progress + "%";
        info.mProgressNumber = progress;
        // 下载分成8个级别，用于显示下载进度动画
        int progressLevel = progress / 14 + 2;
        info.mProgressLevel = progressLevel > 8 ? 8 : progressLevel;
    }

    /*
     * 刷新下载成功的项目状态
     */
    private static void getCompleteStatus(Session session, DownloadInfo info, Cursor cursor) {

        info.mFilePath = cursor.getString(cursor.getColumnIndex(DownloadManager.Impl.COLUMN_DATA));
        if (!TextUtils.isEmpty(info.mFilePath) && new File(info.mFilePath).exists()) {
            info.mProgressLevel = Constants.STATUS_DOWNLOADED;
            ConcurrentHashMap<String, UpgradeInfo> updates = session.getUpdateList();
            if (updates.containsKey(info.mPackageName)) {
                updates.get(info.mPackageName).filePath = info.mFilePath;
            }
        } else {
            ConcurrentHashMap<String, UpgradeInfo> updates = session.getUpdateList();
            if (updates.containsKey(info.mPackageName)) {
                updates.get(info.mPackageName).filePath = "";
            }
            info.mStatus = DownloadManager.Impl.STATUS_REMOVED;
            info.mProgressLevel = Constants.STATUS_NORMAL;
        }
    }

    /*
     * 刷新正在下载中的应用
     */
    synchronized void refreshDownloadApp(Cursor cursor) {

        if (cursor == null) {
            return;
        }

        if (cursor.isClosed()) {
            return;
        }

        // 绑定观察者
        if (mDownloadingCursor == null) {
            mDownloadingCursor = cursor;
            cursor.registerContentObserver(mCursorObserver);
        }

        cursor.requery();

        // 检索有结果
        int downloadingTasks = 0;
        boolean hasUpdate = false;

        HashSet<String> allItems = new HashSet<String>(mDownloadingList.keySet());
        while (cursor.moveToNext()) {

            hasUpdate = true;

            // 获取下载项目的详细信息
            int id = cursor.getInt(cursor.getColumnIndex(DownloadManager.Impl.COLUMN_ID));
            String packageName = cursor.getString(cursor
                    .getColumnIndex(DownloadManager.Impl.COLUMN_PACKAGE_NAME));
            String appName = cursor.getString(cursor
                    .getColumnIndex(DownloadManager.Impl.COLUMN_TITLE));
            int source = cursor.getInt(cursor.getColumnIndex(DownloadManager.Impl.COLUMN_SOURCE));
            String extras = cursor.getString(cursor
                    .getColumnIndex(DownloadManager.Impl.COLUMN_NOTIFICATION_EXTRAS));
            int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.Impl.COLUMN_STATUS));
            int control = cursor.getInt(cursor.getColumnIndex(DownloadManager.Impl.COLUMN_CONTROL));

            DownloadInfo infoItem = mDownloadingList.get(packageName);
            if (infoItem == null) {

                infoItem = mDownloadingList.get(String.valueOf(id));

                if (infoItem != null) {
                    mDownloadingList.remove(infoItem.mKey);
                }
                
                infoItem = newDownloadInfo(id, packageName, appName, source, extras);
                removeStarted(packageName);
            } else {
                removeStarted(packageName);
                infoItem.id = id;
            }
            infoItem.mControl = control;
            allItems.remove(infoItem.mKey);

            // 检查下载项目的异常状态
            if (checkInvalidStatus(infoItem, status)) {
                refreshUpdateApp(infoItem, true);
            }

            // 以下都是正常下载项目的刷新操作
            infoItem.mStatus = status;

            if (DownloadManager.Impl.isStatusRunning(infoItem.mStatus)) {
                // 下载中
                getRunningStatus(infoItem, cursor);
                downloadingTasks++;

            } else if (DownloadManager.Impl.isStatusPending(infoItem.mStatus)) {
                // 下载等待中
                // XXX by Chaos at 2013/2/21 
                // 不使用PENDING状态覆盖掉进度值，因为暂停时，我们需要当前的进度值。
                // 如果被覆盖掉，就无法正确的显示暂停时的进度值。
//              infoItem.mProgressLevel = Constants.STATUS_PENDING;
                if (DownloadManager.Impl.isStatusWaiting(infoItem.mControl)) {
                    // do nothing
                } else {
                    downloadingTasks++;
                }

            } else if (infoItem.mStatus == DownloadManager.Impl.STATUS_SUCCESS) {
                // 下载成功
                getCompleteStatus(this, infoItem, cursor);

            } else if (infoItem.mStatus == DownloadManager.Impl.STATUS_INSTALLED) {
                // 安装成功
                infoItem.mProgressLevel = Constants.STATUS_INSTALLED;

            } else if (DownloadManager.Impl.isStatusError(infoItem.mStatus)) {
                // 下载错误
                getRunningStatus(infoItem, cursor);

            } else {
                // others
                infoItem.mProgressLevel = Constants.STATUS_NORMAL;

            }
            refreshUpdateApp(infoItem, false);
        }

        // 移除废弃的项目
        for (String key : allItems) {
            mDownloadingList.remove(key);
        }

        if (downloadingTasks > 0) {
            setDownloadStatus(true);
        } else {
            setDownloadStatus(false);
        }

        if (hasUpdate) {
            setChanged();
            notifyObservers(mDownloadingList);
        }
    }

    /*
     * 刷新更新的应用状态(下载成功/失败)
     */
    private void refreshUpdateApp(DownloadInfo info, boolean isForce) {

        if (mUpdateApps != null && mUpdateApps.containsKey(info.mKey)) {

            UpgradeInfo item = mUpdateApps.get(info.mKey);

            if (item == null) {
                return;
            }

            if (info.mStatus == DownloadManager.Impl.STATUS_INSTALLED) {
                mUpdateApps.remove(info.mKey);
                return;
            }

            if (isForce) {
                item.status = Constants.STATUS_NORMAL;
                return;
            }

            if (info.mProgressLevel == Constants.STATUS_NORMAL) {
                item.status = Constants.STATUS_NORMAL;
            } else {
                item.status = Constants.STATUS_DOWNLOADING;
            }
        }
    }

    /*
     * 刷新可更新的应用
     */
    private void refreshUpdateApp(Cursor cursor) {
        mUpdateApps = new ConcurrentHashMap<String, UpgradeInfo>();
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                UpgradeInfo info = new UpgradeInfo();
                info.pid = cursor.getString(cursor.getColumnIndex(MarketProvider.COLUMN_P_ID));
                info.pkgName = cursor.getString(cursor
                        .getColumnIndex(MarketProvider.COLUMN_P_PACKAGE_NAME));
                info.versionName = cursor.getString(cursor
                        .getColumnIndex(MarketProvider.COLUMN_P_NEW_VERSION_NAME));
                info.versionCode = cursor.getInt(cursor
                        .getColumnIndex(MarketProvider.COLUMN_P_NEW_VERSION_CODE));
                info.signature = cursor.getString(cursor
                        .getColumnIndex(MarketProvider.COLUMN_P_SIGNATURE));
                mUpdateApps.put(info.pkgName, info);
            }
        }
        if (cursor != null) {
            cursor.close();
        }
    }

    /**
     * 开始下载任务
     */
    public void download(String packageName) {

    }

    /**
     * 本地数据库刷新检查
     * 
     */
    private class DbStatusRefreshTask extends AsyncQueryHandler {

        private final static int DOWNLOAD = 0;
        private final static int UPDATE = 1;

        public DbStatusRefreshTask(ContentResolver cr) {
            super(cr);
        }

        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {

            switch (token) {
            case DOWNLOAD:
                refreshDownloadApp(cursor);
                break;

            case UPDATE:
                refreshUpdateApp(cursor);
                break;

            default:
                break;
            }
        }
    }

    // field about preference
    public static final String P_ISLOGIN = "pref.isLogin";
    public static final String P_MARKET_USERNAME = "pref.market.username";
    public static final String P_MARKET_LOGIN_TYPE = "pref.market.login.type";
    public static final String P_MARKET_PASSWORD = "pref.market.password";
    public static final String P_UID = "pref.uid";
    public static final String P_CARD_VERSION = "pref.card.version";
    public static final String P_UPGRADE_NUM = "pref.upgrade.num";
    public static final String P_UPDATE_APP_NOTIFICATION = "update_app_notification";
    public static final String P_RECOMMEND_APP_NOTIFICATION = "pref.recommend.app";
    public static final String P_RECOMMEND_APP_NOTIFICATION_CHECK_TIME = "pref.recommend.time";
    public static final String P_NOT_DOWNLOAD_IMAGE = "not_download_image";
    public static final String P_PRODUCT_UPDATE_CHECK_TIMESTAMP = "pref.product.update.timestamp";
    public static final String P_UPDATE_ID = "pref.update.id";
    public static final String P_AUTO_DELETE = "delete_after_installation";
    public static final String P_PUSH_MESSAGE_TIMESTAMP = "pref.push.message.timestamp";
    public static final String P_INSTALLPKG = "pref.installed.pkg";

    public static final String P_LPNS_BINDED_DEVID = "pref.lpns.binded.devid";
    public static final String P_LPNS_IS_BINDED = "pref.lpns.is.binded";
    public static final String P_DEFAULT_CHARGE_TYPE = "pref.charge.defaultChargeType";
   
    
    /**判断是否第一次使用返利 */
    public static final String P_IS_FIRST = "pref.is.first.rebate";

    /** 判断是否是第一次登录 */
    public static final String P_ISFIRST_LOGIN = "pref.is.first.login";
    
    /** 判断是否是第二次登录*/
    public static final String P_ISSECOND_LOGIN = "pref.is.second.login";
    
    /** 记录用户最后一次登录的时间*/
    public static final String P_LASTLOGIN = "pref.login.lastlogin.time";

    public static final String P_THEME = "pref.theme";

    // splash info
    public static final String P_SPLASH_TIME = "pref.splash.time";
    public static final String P_SPLASH_ID = "pref.splash.id";

    // version name
    public static final String P_CURRENT_VERSION = "pref.current.version";
    /**判定是否弹出过老虎机的Toast*/
    public static final String P_ISSHOWTIP_ATSECOND = "pref.isshowtip.atsecond";
    /**是否需要显示精品Tab的动画*/
    public static final String P_ISSHOW_HIGHQUALITYTAB_ANIMTION = "pref.isclick.highquality.tab"; 

    public static final String P_TOP_PID = "pref.top.pid";
    private static SharedPreferences mPreference;


    private static final Method sApplyMethod = findApplyMethod();


    @SuppressWarnings({ "rawtypes" })
    private static Method findApplyMethod() {
        try {
            Class cls = SharedPreferences.Editor.class;
            return cls.getMethod("apply");
        } catch (NoSuchMethodException unused) {
            Utils.I("NoSuchMethodException", unused);
        }
        return null;
    }

    /** Use this method to modify preference */
    public static void apply(SharedPreferences.Editor editor) {
        if (sApplyMethod != null) {
            try {
                sApplyMethod.invoke(editor);
                return;
            } catch (InvocationTargetException unused) {
                // fall through
            } catch (IllegalAccessException unused) {
                // fall through
            }
        }
        editor.commit();
    }

    /**
     * Write session value back to preference
     */
    public void writePreference(Pair<String, Object> updateItem) {

        Editor editor = mPreference.edit();

        // the preference key
        final String key = (String) updateItem.first;

        if (P_UID.equals(key) 
                || P_MARKET_USERNAME.equals(key) 
                || P_MARKET_PASSWORD.equals(key)) {

            editor.putString(key, SecurityUtil.encrypt(String.valueOf(updateItem.second)));

        } else if (P_ISLOGIN.equals(key) 
                || P_LPNS_IS_BINDED.equals(key)
                || P_ISFIRST_LOGIN.equals(key) 
                || P_UPDATE_APP_NOTIFICATION.equals(key)
                || P_RECOMMEND_APP_NOTIFICATION.equals(key) 
                || P_NOT_DOWNLOAD_IMAGE.equals(key)
                || P_AUTO_DELETE.equals(key)
                || P_IS_FIRST.equals(key)
                || P_ISSECOND_LOGIN.equals(key)
                || P_ISSHOWTIP_ATSECOND.equals(key)
                || P_ISSHOW_HIGHQUALITYTAB_ANIMTION.equals(key)) {

            editor.putBoolean(key, (Boolean) updateItem.second);

        } else if (P_LPNS_BINDED_DEVID.equals(key) 
                || P_DEFAULT_CHARGE_TYPE.equals(key)
                || P_RECOMMEND_APP_NOTIFICATION_CHECK_TIME.equals(key)
                || P_PUSH_MESSAGE_TIMESTAMP.equals(key)
                || P_TOP_PID.equals(key)) {

            editor.putString(key, (String) updateItem.second);

        } else if (P_UPGRADE_NUM.equals(key) 
                || P_CARD_VERSION.equals(key)
                || P_CURRENT_VERSION.equals(key) 
                || P_THEME.equals(key)) {

            editor.putInt(key, (Integer) updateItem.second);

        } else if (P_PRODUCT_UPDATE_CHECK_TIMESTAMP.equals(key) 
                || P_SPLASH_TIME.equals(key)
                || P_SPLASH_ID.equals(key) 
                || P_UPDATE_ID.equals(key)
                || P_LASTLOGIN.equals(key)) {

            editor.putLong(key, (Long) updateItem.second);

        } else {
            return;
        }

        // update the preference
        apply(editor);
    }
    
    /**
     * 阻止因多次点击造成一个应用重复下载bug的条件方法
     * @param key
     * @return
     */
    public synchronized boolean addDownload(Object key) {
        if (mPendingDownloadList.contains(key)) {
            return false;
        } else {
            mPendingDownloadList.add(key);
            if (!mDownloadingList.contains(key)) {
                return true;
            } else {
                return false;
            }
        }
    }
    
    /**
     * 移除PendingDownloadList内的记录项
     * @param key
     */
    public void removeStarted(Object key) {

        if (key == null) {
            return;
        }

        if (key instanceof String) {
            String keyString = (String) key;
            mPendingDownloadList.remove(keyString);
        }
    }
    
    public boolean isFirstRebate() {
      return mPreference.getBoolean(P_IS_FIRST, true);
    }

    public void setRebated() {
      writePreference(new Pair<String, Object>(P_IS_FIRST, false));
    }

    public long getUserLastLoginTime() {
        return this.userLastLoginTime;
    }

    public void setUserLastLoginTime(long userLastLoginTime) {
        this.userLastLoginTime = userLastLoginTime;
        writePreference(new Pair<String,Object>(P_LASTLOGIN,userLastLoginTime));
    }
    
    public void setIsShowTipAtSecond(boolean alreadyShow){
        this.mIsShowTipAtSecondLogin = alreadyShow;
        writePreference(new Pair<String,Object>(P_ISSHOWTIP_ATSECOND,alreadyShow));
    }
    
    public boolean getIsShowTipAtSecond(){
        return this.mIsShowTipAtSecondLogin;
    }
    
    /**
     * 是否显示精品Tab的动画
     * @param isClicked
     */
    public void setIsShowHighQualityTabAnimtion(boolean show){
        this.mIsShowHighQualityTabAnimtion = show;
        writePreference(new Pair<String, Object>(P_ISSHOW_HIGHQUALITYTAB_ANIMTION, show));
    }
    
    public boolean getIsShowHighQualityTabAnimation(){
        return this.mIsShowHighQualityTabAnimtion;
    }
}